-
Notifications
You must be signed in to change notification settings - Fork 506
Fix imprecise types after assignment when strict-types=1 #3945
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
self::$i = getInt(); | ||
assertType('float|int', self::$i); // could be int | ||
assertNativeType('float|int', self::$i); // could be int | ||
|
||
$this->impureCall(); | ||
assertType('float|int', self::$i); | ||
assertNativeType('float|int', self::$i); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the impureCall
does not make the scope forget about already narrowed types of static property fetches, therefore we don't narrow in this scenario with this PR.
-> we only improve the instance-call case, because non-static fetches will be forgotten as the test in NarrowsNativeUnion
proves
This pull request has been marked as ready for review. |
I've analyzed this situation. It's much more clear what's going on in this code sample: https://phpstan.org/r/903891e3-323a-494b-83a0-27aff806eb88 It's happening because IntegerType::toCoercedArgumentType() returns But what should happen first is even before we call toCoercedArgumentType, we should check whether the original value (ConstantIntegerType 1) is a subtype of the native property type.
Because 1) matches, we don't need to call toCoercedArgumentType at all. We simply assign the value as it is. To get a list of types to compare, we can call TypeUtils::flattenTypes on the native property type. That way we can compare it in a foreach even when there's just a single type like What's nice about this is that it can solve the problem for strict_types = 0 as well. The workflow should be:
If done correctly, this will remove the need for #3943 as well. |
src/Analyser/NodeScopeResolver.php
Outdated
TypeCombinator::intersect($assignedNativeType->toCoercedArgumentType(true), $propertyNativeType), | ||
); | ||
} else { | ||
$scope = $scope->assignExpression($var, $assignedExprType, $assignedNativeType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is gonna get executed if assigned type is compatible or if we're not in declare strict types. That's not what we want. We only want to execute this if assigned type is compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so what to execute if not compatible and not strict types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it works now as you imagined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not compatible and not strict types, we currently can't do anything. Once we properly implement toCoercedArgumentType in the whole typesystem, then we can use it same way we use it now with strict_types=1.
$this->impureCall(); | ||
assertType('int', self::$i); // should be float|int | ||
assertNativeType('int', self::$i); // should be float|int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a unrelated bug, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, probably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix in #3950
@@ -398,7 +398,7 @@ private static function isFileLintSkipped(string $file): bool | |||
|
|||
@fclose($f); | |||
|
|||
if (preg_match('~<?php\\s*\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m) === 1) { | |||
if (preg_match('~<?php\\s*(?:declare\\s*\([^)]+\)\\s*;\\s*)?\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m) === 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't compatible with how php-parallel-lint works.
@@ -0,0 +1,88 @@ | |||
<?php declare(strict_types = 0); // lint >= 8.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first line has to be <?php // lint >= 8.1
and the declare statement has to be on line 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!
$this->impureCall(); | ||
assertType('int', self::$i); // should be float|int | ||
assertNativeType('int', self::$i); // should be float|int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, probably.
Thank you! |
work with coecered type only when the assigned one is incompatible with the natively defined property type
closes phpstan/phpstan#12902
supercedes #3943